/*******************************************************************
 *
 * Copyright (C), 2021-2022, Letstrong, All rights reserved.
 *
 * Author      : 
 * Create Time : 2020-12-14
 * Version     : v0.1
 * Description : 
 *******************************************************************/
 
/*----------------------------Head file----------------------------*/
#include "log.h"
#include <stdint.h>        // standard integer definition
#include <string.h>        // string manipulation
#include <stddef.h>        // standard definition
#include "lld_evt.h"
#include "hal_utc.h"
#include "user_config.h"
#include "sw_timer_task.h"
#include "app_global_data.h"

/*----------------------------macro file---------------------------*/
#define	IsLeapYear(yr)	(!((yr) % 400) || (((yr) % 100) && !((yr) % 4)))
#define	YearLength(yr)	(IsLeapYear(yr) ? 366 : 365)

#define	BEGYEAR	        1970     // UTC started at 00:00:00 January 1, 1970

#define	DAY             86400UL  // 24 hours * 60 minutes * 60 seconds

#define MAX_BASETIME_COUNT         0x7FFFFFF

// (MAXCALCTICKS * 5) + (max remainder) must be <= (RW MAX basetimecnt)[26 - 0], 0x7FFFFFF
// so: (0x1999998 * 5) + 7 <= 0x7FFFFFF
#define MAXCALCTICKS  ((uint32_t)(0x1999998))

/*----------------------------type define--------------------------*/

/*----------------------------var define---------------------------*/
static uint32_t previousRwTimerTick = 0;
static uint32_t remUsTicks = 0;
static uint32_t timeMSec = 0;
uint32_t utc_timeSeconds = 0;

/*-------------------------func declaration------------------------*/
static void utc_clock_update(uint32_t elapsedMSec);

/*-----------------------------------------------------------------*/

uint32_t hal_utc_seconds_get(void)
{
	hal_utc_update();
	return (utc_timeSeconds);
}

void hal_utc_seconds_set(uint32_t newTime)
{
	hal_utc_update();
	utc_timeSeconds = newTime;
    g_app_data.tmp.utc = newTime;
	utc_timer_start();
}


/*********************************************************************
 * @fn      utc_clock_update
 *
 * @brief   Updates the OSAL Clock time with elapsed milliseconds.
 *
 * @param   elapsedMSec - elapsed milliseconds
 *
 * @return  none
 */
static void utc_clock_update(uint32_t elapsedMSec)
{
	// Add elapsed milliseconds to the saved millisecond portion of time
	timeMSec += elapsedMSec;

	// Roll up milliseconds to the number of seconds
	if (timeMSec >= 1000)
	{
		utc_timeSeconds += timeMSec / 1000;
		timeMSec = timeMSec % 1000;
	}
}

void hal_utc_update(void)
{
	uint32_t tmp;
	uint32_t ticks625us;
	uint32_t elapsedMSec = 0;

	// Get the free-running count of 625us timer ticks
	tmp = lld_evt_time_get();
	//UART_PRINTF("tmp->%lu\r\n", tmp);

	if(tmp != previousRwTimerTick)
	{
		if(tmp > previousRwTimerTick)
		{
			// Calculate the elapsed ticks of the free-running timer.
			ticks625us = tmp - previousRwTimerTick;
		}
		else
		{
			ticks625us = tmp + MAX_BASETIME_COUNT - previousRwTimerTick;
		}

		// Store the LL Timer tick count for the next time through this function.
		previousRwTimerTick = tmp;

		/* It is necessary to loop to convert the usecs to msecs in increments so as
		 * not to overflow the 32-bit variables.
		 */
		while(ticks625us > MAXCALCTICKS)
		{
			ticks625us -= MAXCALCTICKS;
			elapsedMSec += MAXCALCTICKS * 5 / 8;
			remUsTicks += MAXCALCTICKS * 5 % 8;
		}

		// update converted number with remaining ticks from loop and the
		// accumulated remainder from loop
		tmp = (ticks625us * 5) + remUsTicks;

		// Convert the 625 us ticks into milliseconds and a remainder
		elapsedMSec += tmp / 8;
		remUsTicks = tmp % 8;

		// Update Clock
		if(elapsedMSec)
		{
			utc_clock_update( elapsedMSec );
		}
	}
}



#if(UART_PRINTF_EN)

/*********************************************************************
 * @fn      monthLength
 *
 * @param   lpyr - 1 for leap year, 0 if not
 *
 * @param   mon - 0 - 11 (jan - dec)
 *
 * @return  number of days in specified month
 */
static uint8_t monthLength(uint8_t lpyr, uint8_t mon)
{
	uint8_t days = 31;

	if(mon == 1) // feb
	{
		days = (28 + lpyr);
	}
	else
	{
		if(mon > 6) // aug-dec
		{
			mon--;
		}

		if(mon & 1)
		{
			days = 30;
		}
	}

	return ( days );
}


/*********************************************************************
 * @fn      utc_convert_rtc_time
 *
 * @brief   Converts uint32_t to hal_rtc_t
 *
 * @param   tm - pointer to breakdown struct
 *
 * @param   secTime - number of seconds since 0 hrs, 0 minutes,
 *          0 seconds, on the 1st of January 2000 UTC
 *
 * @return  none
 */
static void utc_convert_rtc_time(hal_rtc_t *tm, uint32_t secTime)
{
	// calculate the time less than a day - hours, minutes, seconds
	{
		uint32_t day = secTime % DAY;
		tm->hour = day / 3600UL;
		tm->seconds = day % 60UL;
		tm->minutes = (day % 3600UL) / 60UL;
	}

	// Fill in the calendar - day, month, year
	{
		uint16_t numDays = secTime / DAY;
		tm->year = BEGYEAR;
		while ( numDays >= YearLength( tm->year ) )
		{
			numDays -= YearLength( tm->year );
			tm->year++;
		}

		tm->month = 0;
		while ( numDays >= monthLength( IsLeapYear( tm->year ), tm->month ) )
		{
			numDays -= monthLength( IsLeapYear( tm->year ), tm->month );
			tm->month++;
		}

		tm->day = numDays;
	}
}



#define TIMEZONE_SECS	(8*60*60)
void utc_get_time(void)
{
	hal_rtc_t tm;
	utc_convert_rtc_time(&tm, hal_utc_seconds_get()+TIMEZONE_SECS);
	tm.month++;
	tm.day++;
	
	LOG_DEBUG("Time is %d-%d-%d, %d:%d:%d\n", tm.year, tm.month, tm.day, tm.hour, tm.minutes, tm.seconds);
}
#endif	//#if(UART_PRINTF_EN)

